package com.xbk.seckill.stock.application.service.impl;

import cn.hutool.core.util.BooleanUtil;
import com.alibaba.fastjson.JSON;
import com.xbk.seckill.common.cache.distribute.DistributedCacheService;
import com.xbk.seckill.common.cache.model.SeckillBusinessCache;
import com.xbk.seckill.common.constants.SeckillConstants;
import com.xbk.seckill.common.exception.ErrorCode;
import com.xbk.seckill.common.exception.SeckillException;
import com.xbk.seckill.common.lock.DistributedLock;
import com.xbk.seckill.common.lock.factory.DistributedLockFactory;
import com.xbk.seckill.common.model.dto.SeckillStockDTO;
import com.xbk.seckill.common.model.message.ErrorMessage;
import com.xbk.seckill.common.model.message.TxMessage;
import com.xbk.seckill.mq.MessageSenderService;
import com.xbk.seckill.stock.application.cache.SeckillStockBucketCacheService;
import com.xbk.seckill.stock.application.model.command.SeckillStockBucketWrapperCommand;
import com.xbk.seckill.stock.application.model.dto.SeckillStockBucketDTO;
import com.xbk.seckill.stock.application.service.SeckillStockBucketArrangementService;
import com.xbk.seckill.stock.application.service.SeckillStockBucketService;
import com.xbk.seckill.stock.domain.model.dto.SeckillStockBucketDeduction;
import com.xbk.seckill.stock.domain.service.SeckillStockBucketDomainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * 分桶库存服务
 *
 * @author xiexu
 * @date 2025/3/8 22:15
 */
@Slf4j
@Service
public class SeckillStockBucketServiceImpl implements SeckillStockBucketService {

    @Autowired
    private DistributedLockFactory distributedLockFactory;

    @Autowired
    private SeckillStockBucketArrangementService seckillStockBucketArrangementService;

    @Autowired
    private SeckillStockBucketCacheService seckillStockBucketCacheService;

    @Autowired
    private MessageSenderService messageSenderService;

    @Autowired
    private DistributedCacheService distributedCacheService;

    @Autowired
    private SeckillStockBucketDomainService seckillStockBucketDomainService;

    /**
     * 编排库存分桶
     *
     * @param userId
     * @param stockBucketWrapperCommand
     */
    @Override
    public void arrangeStockBuckets(Long userId, SeckillStockBucketWrapperCommand stockBucketWrapperCommand) {
        if (userId == null || stockBucketWrapperCommand == null) {
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        stockBucketWrapperCommand.setUserId(userId);
        if (stockBucketWrapperCommand.isEmpty()) {
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        log.info("arrangeBuckets|编排库存分桶|{}", JSON.toJSON(stockBucketWrapperCommand));
        String userKey = SeckillConstants.getKey(SeckillConstants.GOODS_BUCKET_ARRANGEMENT_KEY,
                String.valueOf(stockBucketWrapperCommand.getUserId()));
        String lockKey = SeckillConstants.getKey(userKey,
                String.valueOf(stockBucketWrapperCommand.getGoodsId()));
        // 获取分布式锁实例
        DistributedLock lock = distributedLockFactory.getDistributedLock(lockKey);
        try {
            // 尝试获取锁
            boolean isLock = lock.tryLock();
            if (!isLock) {
                throw new SeckillException(ErrorCode.FREQUENTLY_ERROR);
            }
            // 获取到锁，编排商品分桶库存
            seckillStockBucketArrangementService.arrangeStockBuckets(
                    // 商品id
                    stockBucketWrapperCommand.getGoodsId(),
                    // 库存总量
                    stockBucketWrapperCommand.getStockBucketCommand().getTotalStock(),
                    // 分桶数量
                    stockBucketWrapperCommand.getStockBucketCommand().getBucketsQuantity(),
                    // 分桶编排模式, 1：总量模式  2：增量模式
                    stockBucketWrapperCommand.getStockBucketCommand().getArrangementMode()
            );
            log.info("arrangeStockBuckets|库存编排完成|{}", stockBucketWrapperCommand.getGoodsId());
        } catch (SeckillException e) {
            log.error("arrangeStockBuckets|库存编排失败|{}", stockBucketWrapperCommand.getGoodsId(), e);
            throw e;
        } catch (Exception e) {
            log.error("arrangeStockBuckets|库存编排错误|{}", stockBucketWrapperCommand.getGoodsId(), e);
            throw new SeckillException(ErrorCode.BUCKET_CREATE_FAILED);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 获取库存分桶数据
     *
     * @param goodsId
     * @param version
     * @return
     */
    @Override
    public SeckillStockBucketDTO getTotalStockBuckets(Long goodsId, Long version) {
        if (goodsId == null) {
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        log.info("stockBucketsSummary|获取库存分桶数据|{}", goodsId);
        return seckillStockBucketArrangementService.getSeckillStockBucketDTO(goodsId, version);
    }

    /**
     * 获取库存分桶
     *
     * @param goodsId
     * @param version
     * @return
     */
    @Override
    public SeckillBusinessCache<Integer> getAvailableStock(Long goodsId, Long version) {
        if (goodsId == null) {
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        return seckillStockBucketCacheService.getAvailableStock(goodsId, version);
    }

    /**
     * 扣减商品库存
     *
     * @param goodsId
     * @param version
     * @return
     */
    @Override
    public SeckillBusinessCache<SeckillStockDTO> getSeckillStock(Long goodsId, Long version) {
        if (goodsId == null) {
            throw new SeckillException(ErrorCode.PARAMS_INVALID);
        }
        return seckillStockBucketCacheService.getSeckillStock(goodsId, version);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean decreaseStock(TxMessage txMessage) {
        Boolean decrementStock = distributedCacheService.hasKey(SeckillConstants.getKey(SeckillConstants.STOCK_TX_KEY, String.valueOf(txMessage.getTxNo())));
        if (BooleanUtil.isTrue(decrementStock)) {
            log.info("updateAvailableStock|秒杀商品微服务已经扣减过库存|{}", txMessage.getTxNo());
            return true;
        }
        boolean isUpdate = false;
        try {
            SeckillStockBucketDeduction seckillStockBucketDeduction = new SeckillStockBucketDeduction(txMessage.getGoodsId(), txMessage.getQuantity(), txMessage.getUserId(), txMessage.getBucketSerialNo());
            // 扣减数据库商品库存
            isUpdate = seckillStockBucketDomainService.decreaseStock(seckillStockBucketDeduction);
            // 成功扣减库存成功
            if (isUpdate) {
                String cacheKey = SeckillConstants.getKey(SeckillConstants.STOCK_TX_KEY, String.valueOf(txMessage.getTxNo()));
                distributedCacheService.put(cacheKey, txMessage.getTxNo(), SeckillConstants.TX_LOG_EXPIRE_DAY, TimeUnit.DAYS);
            } else {
                // 发送失败消息给订单微服务
                ErrorMessage errorMessage = getErrorMessage(txMessage);
                messageSenderService.send(errorMessage);
            }
        } catch (Exception e) {
            isUpdate = false;
            log.error("decreaseStock|抛出异常|{},{}", txMessage.getTxNo(), e.getMessage());
            // 发送失败消息给订单微服务
            messageSenderService.send(getErrorMessage(txMessage));
        }
        return isUpdate;
    }

    /**
     * 发送给订单微服务的错误消息
     */
    private ErrorMessage getErrorMessage(TxMessage txMessage) {
        return new ErrorMessage(
                SeckillConstants.TOPIC_ERROR_MSG,
                txMessage.getTxNo(),
                txMessage.getGoodsId(),
                txMessage.getQuantity(),
                txMessage.getPlaceOrderType(),
                txMessage.getException(),
                txMessage.getBucketSerialNo(),
                txMessage.getUserId(),
                txMessage.getOrderTaskId()
        );
    }
}
